/*
 * CODE GENERATED AUTOMATICALLY WITH github.com/alanctgardner/gogen-avro
 * THIS FILE SHOULD NOT BE EDITED BY HAND
 *
 * SOURCE:
 *     tcp.avsc
 */

package avro

import (
	"fmt"
	"io"
	"math"
)

type ByteReader interface {
	ReadByte() (byte, error)
}

type ByteWriter interface {
	Grow(int)
	WriteByte(byte) error
}

type StringWriter interface {
	WriteString(string) (int, error)
}

func encodeInt(w io.Writer, byteCount int, encoded uint64) error {
	var err error
	var bb []byte
	bw, ok := w.(ByteWriter)
	// To avoid reallocations, grow capacity to the largest possible size
	// for this integer
	if ok {
		bw.Grow(byteCount)
	} else {
		bb = make([]byte, 0, byteCount)
	}

	if encoded == 0 {
		if bw != nil {
			err = bw.WriteByte(0)
			if err != nil {
				return err
			}
		} else {
			bb = append(bb, byte(0))
		}
	} else {
		for encoded > 0 {
			b := byte(encoded & 127)
			encoded = encoded >> 7
			if !(encoded == 0) {
				b |= 128
			}
			if bw != nil {
				err = bw.WriteByte(b)
				if err != nil {
					return err
				}
			} else {
				bb = append(bb, b)
			}
		}
	}
	if bw == nil {
		_, err := w.Write(bb)
		return err
	}
	return nil

}

func readBool(r io.Reader) (bool, error) {
	var b byte
	var err error
	if br, ok := r.(ByteReader); ok {
		b, err = br.ReadByte()
	} else {
		bs := make([]byte, 1)
		_, err = io.ReadFull(r, bs)
		if err != nil {
			return false, err
		}
		b = bs[0]
	}
	return b == 1, nil
}

func readInt(r io.Reader) (int32, error) {
	var v int
	buf := make([]byte, 1)
	for shift := uint(0); ; shift += 7 {
		if _, err := io.ReadFull(r, buf); err != nil {
			return 0, err
		}
		b := buf[0]
		v |= int(b&127) << shift
		if b&128 == 0 {
			break
		}
	}
	datum := (int32(v>>1) ^ -int32(v&1))
	return datum, nil
}

func readLong(r io.Reader) (int64, error) {
	var v uint64
	buf := make([]byte, 1)
	for shift := uint(0); ; shift += 7 {
		if _, err := io.ReadFull(r, buf); err != nil {
			return 0, err
		}
		b := buf[0]
		v |= uint64(b&127) << shift
		if b&128 == 0 {
			break
		}
	}
	datum := (int64(v>>1) ^ -int64(v&1))
	return datum, nil
}

func readNull(_ io.Reader) (interface{}, error) {
	return nil, nil
}

func readString(r io.Reader) (string, error) {
	len, err := readLong(r)
	if err != nil {
		return "", err
	}

	// makeslice can fail depending on available memory.
	// We arbitrarily limit string size to sane default (~2.2GB).
	if len < 0 || len > math.MaxInt32 {
		return "", fmt.Errorf("string length out of range: %d", len)
	}

	bb := make([]byte, len)
	_, err = io.ReadFull(r, bb)
	if err != nil {
		return "", err
	}
	return string(bb), nil
}

func readTCP(r io.Reader) (*TCP, error) {
	var str = &TCP{}
	var err error
	str.Length, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.Time, err = readUnionLongNull(r)
	if err != nil {
		return nil, err
	}
	str.SrcMAC, err = readUnionStringNull(r)
	if err != nil {
		return nil, err
	}
	str.DstMAC, err = readUnionStringNull(r)
	if err != nil {
		return nil, err
	}
	str.NetworkProtocol, err = readUnionStringNull(r)
	if err != nil {
		return nil, err
	}
	str.IHL, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.TOS, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.Id, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.Flags, err = readUnionStringNull(r)
	if err != nil {
		return nil, err
	}
	str.FragOffset, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.TTL, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.SrcIP, err = readUnionStringNull(r)
	if err != nil {
		return nil, err
	}
	str.DstIP, err = readUnionStringNull(r)
	if err != nil {
		return nil, err
	}
	str.TransportProtol, err = readUnionStringNull(r)
	if err != nil {
		return nil, err
	}
	str.SrcPort, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.DstPort, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.Seq, err = readUnionLongNull(r)
	if err != nil {
		return nil, err
	}
	str.Ack, err = readUnionLongNull(r)
	if err != nil {
		return nil, err
	}
	str.FIN, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.SYN, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.RST, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.PSH, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.ACK, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.URG, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.ECE, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.CWR, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.NS, err = readUnionBoolNull(r)
	if err != nil {
		return nil, err
	}
	str.Window, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}
	str.CheckSum, err = readUnionIntNull(r)
	if err != nil {
		return nil, err
	}

	return str, nil
}

func readUnionBoolNull(r io.Reader) (UnionBoolNull, error) {
	field, err := readLong(r)
	var unionStr UnionBoolNull
	if err != nil {
		return unionStr, err
	}
	unionStr.UnionType = UnionBoolNullTypeEnum(field)
	switch unionStr.UnionType {
	case UnionBoolNullTypeEnumBool:
		val, err := readBool(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.Bool = val
	case UnionBoolNullTypeEnumNull:
		val, err := readNull(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.Null = val

	default:
		return unionStr, fmt.Errorf("Invalid value for UnionBoolNull")
	}
	return unionStr, nil
}

func readUnionIntNull(r io.Reader) (UnionIntNull, error) {
	field, err := readLong(r)
	var unionStr UnionIntNull
	if err != nil {
		return unionStr, err
	}
	unionStr.UnionType = UnionIntNullTypeEnum(field)
	switch unionStr.UnionType {
	case UnionIntNullTypeEnumInt:
		val, err := readInt(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.Int = val
	case UnionIntNullTypeEnumNull:
		val, err := readNull(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.Null = val

	default:
		return unionStr, fmt.Errorf("Invalid value for UnionIntNull")
	}
	return unionStr, nil
}

func readUnionLongNull(r io.Reader) (UnionLongNull, error) {
	field, err := readLong(r)
	var unionStr UnionLongNull
	if err != nil {
		return unionStr, err
	}
	unionStr.UnionType = UnionLongNullTypeEnum(field)
	switch unionStr.UnionType {
	case UnionLongNullTypeEnumLong:
		val, err := readLong(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.Long = val
	case UnionLongNullTypeEnumNull:
		val, err := readNull(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.Null = val

	default:
		return unionStr, fmt.Errorf("Invalid value for UnionLongNull")
	}
	return unionStr, nil
}

func readUnionStringNull(r io.Reader) (UnionStringNull, error) {
	field, err := readLong(r)
	var unionStr UnionStringNull
	if err != nil {
		return unionStr, err
	}
	unionStr.UnionType = UnionStringNullTypeEnum(field)
	switch unionStr.UnionType {
	case UnionStringNullTypeEnumString:
		val, err := readString(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.String = val
	case UnionStringNullTypeEnumNull:
		val, err := readNull(r)
		if err != nil {
			return unionStr, err
		}
		unionStr.Null = val

	default:
		return unionStr, fmt.Errorf("Invalid value for UnionStringNull")
	}
	return unionStr, nil
}

func writeBool(r bool, w io.Writer) error {
	var b byte
	if r {
		b = byte(1)
	}

	var err error
	if bw, ok := w.(ByteWriter); ok {
		err = bw.WriteByte(b)
	} else {
		bb := make([]byte, 1)
		bb[0] = b
		_, err = w.Write(bb)
	}
	if err != nil {
		return err
	}
	return nil
}

func writeInt(r int32, w io.Writer) error {
	downShift := uint32(31)
	encoded := uint64((uint32(r) << 1) ^ uint32(r>>downShift))
	const maxByteSize = 5
	return encodeInt(w, maxByteSize, encoded)
}

func writeLong(r int64, w io.Writer) error {
	downShift := uint64(63)
	encoded := uint64((r << 1) ^ (r >> downShift))
	const maxByteSize = 10
	return encodeInt(w, maxByteSize, encoded)
}

func writeNull(_ interface{}, _ io.Writer) error {
	return nil
}

func writeString(r string, w io.Writer) error {
	err := writeLong(int64(len(r)), w)
	if err != nil {
		return err
	}
	if sw, ok := w.(StringWriter); ok {
		_, err = sw.WriteString(r)
	} else {
		_, err = w.Write([]byte(r))
	}
	return err
}

func writeTCP(r *TCP, w io.Writer) error {
	var err error
	err = writeUnionIntNull(r.Length, w)
	if err != nil {
		return err
	}
	err = writeUnionLongNull(r.Time, w)
	if err != nil {
		return err
	}
	err = writeUnionStringNull(r.SrcMAC, w)
	if err != nil {
		return err
	}
	err = writeUnionStringNull(r.DstMAC, w)
	if err != nil {
		return err
	}
	err = writeUnionStringNull(r.NetworkProtocol, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.IHL, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.TOS, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.Id, w)
	if err != nil {
		return err
	}
	err = writeUnionStringNull(r.Flags, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.FragOffset, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.TTL, w)
	if err != nil {
		return err
	}
	err = writeUnionStringNull(r.SrcIP, w)
	if err != nil {
		return err
	}
	err = writeUnionStringNull(r.DstIP, w)
	if err != nil {
		return err
	}
	err = writeUnionStringNull(r.TransportProtol, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.SrcPort, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.DstPort, w)
	if err != nil {
		return err
	}
	err = writeUnionLongNull(r.Seq, w)
	if err != nil {
		return err
	}
	err = writeUnionLongNull(r.Ack, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.FIN, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.SYN, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.RST, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.PSH, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.ACK, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.URG, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.ECE, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.CWR, w)
	if err != nil {
		return err
	}
	err = writeUnionBoolNull(r.NS, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.Window, w)
	if err != nil {
		return err
	}
	err = writeUnionIntNull(r.CheckSum, w)
	if err != nil {
		return err
	}

	return nil
}

func writeUnionBoolNull(r UnionBoolNull, w io.Writer) error {
	err := writeLong(int64(r.UnionType), w)
	if err != nil {
		return err
	}
	switch r.UnionType {
	case UnionBoolNullTypeEnumBool:
		return writeBool(r.Bool, w)
	case UnionBoolNullTypeEnumNull:
		return writeNull(r.Null, w)

	}
	return fmt.Errorf("Invalid value for UnionBoolNull")
}

func writeUnionIntNull(r UnionIntNull, w io.Writer) error {
	err := writeLong(int64(r.UnionType), w)
	if err != nil {
		return err
	}
	switch r.UnionType {
	case UnionIntNullTypeEnumInt:
		return writeInt(r.Int, w)
	case UnionIntNullTypeEnumNull:
		return writeNull(r.Null, w)

	}
	return fmt.Errorf("Invalid value for UnionIntNull")
}

func writeUnionLongNull(r UnionLongNull, w io.Writer) error {
	err := writeLong(int64(r.UnionType), w)
	if err != nil {
		return err
	}
	switch r.UnionType {
	case UnionLongNullTypeEnumLong:
		return writeLong(r.Long, w)
	case UnionLongNullTypeEnumNull:
		return writeNull(r.Null, w)

	}
	return fmt.Errorf("Invalid value for UnionLongNull")
}

func writeUnionStringNull(r UnionStringNull, w io.Writer) error {
	err := writeLong(int64(r.UnionType), w)
	if err != nil {
		return err
	}
	switch r.UnionType {
	case UnionStringNullTypeEnumString:
		return writeString(r.String, w)
	case UnionStringNullTypeEnumNull:
		return writeNull(r.Null, w)

	}
	return fmt.Errorf("Invalid value for UnionStringNull")
}
